#include <map>
#include <utility>
#include "room.h"
#include "./../Tools/utility.h"
#include "./../Tools/myLog.h"

using namespace std;

template <typename K, typename V, class C, class A>
ostream &operator << (ostream &os, map<K,V,C,A> const& m)
{
	os << "{ ";
	typename map<K,V,C,A>::const_iterator p;
	for (p = m.begin(); p != m.end(); ++p) {
		os << p->first << ":" << p->second << ", ";
	}
	return os << "}";
}

typedef map<string, room> mapAreaRooms;
typedef map<string, mapAreaRooms> mapArea;
static mapArea Area;

room::room(string strRoom)
{
	isLoadSuccess = false;
	if (strRoom != "")
	{
		string strRoomArea;
		string strRoomID;
		utility::splistString(strRoom, '.', &strRoomArea, &strRoomID);
		
		char txt[255];
		ifstream inFile;
		string filename = "./RoomFolder/" + strRoomArea + "/" + strRoomID;
		inFile.open(filename.c_str());
		if (!inFile) {
			string strmsg = "Unable to open room file [ " + filename + " ]";
			myLog m = myLog("room");
			m.writelog(strmsg.c_str());
			cout << strmsg << endl;
		}
		else
		{
			isLoadSuccess = true;
			while(!inFile.eof())
			{
				inFile.getline(txt,255,'\n');
				if (! PutIntoHashTable(txt))
				{
					isLoadSuccess = false;
					break;
				}
			}
		}
		inFile.close();
	}
}

room::~room()
{
}

void room::dump(string strRoom)
{
	room *tmpRoom;
	tmpRoom = room::GetRoomFromStrRoom(strRoom);
	
	
	cout << "isLoadSuccess : " << tmpRoom->isLoadSuccess << endl;
	map<string, string>::iterator itRoomData;
	cout << "RoomData" << endl;
	for (itRoomData = tmpRoom->RoomData.begin(); itRoomData != tmpRoom->RoomData.end(); ++itRoomData) {
		cout << "Area : " << itRoomData->first << endl;
		cout << "RoomID : " << itRoomData->second << endl;
	}
	//RoomMember
	map<string, User*>::iterator itRoomMember;
	cout << "RoomMember" << endl;
	for (itRoomMember = tmpRoom->RoomMember.begin(); itRoomMember != tmpRoom->RoomMember.end(); ++itRoomMember) {
		cout << "Area : " << itRoomMember->first << endl;
		cout << "RoomID : " << itRoomMember->second << endl;
	}
}

bool room::PutIntoHashTable(char chr[255])
{
	if (chr[0] != '\0')
	{
		int t;
		string strKey, strValue;
		string strSetting;
		strSetting = chr;
		t = strSetting.find('=');
		if (t != -1)//there are " = ", it mean normal setting.
		{
			strKey = strSetting.substr(0,t);
			strValue = strSetting.substr(t+1,strSetting.length());
			strKey = utility::string2upper(utility::trim(strKey));
			strValue = utility::trim(strValue);
			RoomData[strKey] = strValue;
			lastcmd = strKey;
		}else{//ther are no " = ", it mean continul append last command.
			if (lastcmd != "")
			{
				RoomData[lastcmd] += chr;
			}else{
				string strmsg = "Error happend in load room data.";
				myLog m = myLog("room");
				m.writelog(strmsg.c_str());
				cout << strmsg << endl;
				return false;
			}
		}
	}
	return true;
}


//**************************** Static functions ****************************
bool room::init()
{
	room::loadRoom("Jail.0001");
	return true;
}

string room::whereIsTheUser(string strUserName)
{
	mapArea::iterator itArea;
	mapAreaRooms::iterator itRoom;
	map<string, User*>::iterator itRM;
	
	for ( itArea=Area.begin() ; itArea != Area.end(); itArea++)
	{
		for ( itRoom=(itArea->second).begin() ; itRoom != (itArea->second).end(); itRoom++)
		{
			itRM = (itRoom->second).RoomMember.find(strUserName);
			if ( itRM != (itRoom->second).RoomMember.end() )
			{
				return itArea->first + "." + itRoom->first;
			}
		}
	}
	return "";
}

void room::BroadcastMsg(User* u, string strRoom, string message)
{
	//get the Room object.
	room* pr = room::GetRoomFromStrRoom(strRoom);
	if (!(*pr).isLoadSuccess)
	{
		cout << "[" << strRoom << "] Room is not exist";
	}
	else
	{
		//Send the message to all user in the room.
		map<string, User*>::iterator it;
		for ( it=(*pr).RoomMember.begin() ; it != (*pr).RoomMember.end(); it++ )
		{
			// Do not send message to userself.
			//if ((*it).first != (*u).UserData[ Const.userName ])
			//{
				(*(*it).second).OutputMessage(strRoom);
			//}
		}
	}
}

bool room::MoveInto(string strRoom, User* u)
{
	room* pRi = room::GetRoomFromStrRoom(strRoom);
	if (!(*pRi).isLoadSuccess)
	{
		(*u).OutputMessage("Target Room is not exist and can't reload from file.");
	}
	else
	{
		//Source Room is exist
		room* pRo = room::GetRoomFromStrRoom(strRoom);
		//add connection to map object in target room
		(*pRi).RoomMember[(*u).UserData[ Const.userName ]] = u;
		//send message to ri and ro room
		map<string, User*>::iterator it;
		for ( it=(*pRi).RoomMember.begin() ; it != (*pRi).RoomMember.end(); it++ )
		{
			if ((*it).first != (*u).UserData[ Const.userName ])
			{
				(*(*it).second).OutputMessage("There are some body into this room");
			}
		}
		for ( it=(*pRo).RoomMember.begin() ; it != (*pRo).RoomMember.end(); it++ )
		{
			if ((*it).first != (*u).UserData[ Const.userName ])
			{
				(*(*it).second).OutputMessage("There are some body leave this room");
			}
		}
		//move connection from source room.
		/*
		it = (*pRi).RoomMember.find((*u).UserProfile.userName);
		(*pRi).RoomMember.erase(it);
		*/
	}
}

room* room::GetRoomFromStrRoom(string strRoom)
{
	string strRoomArea;
	string strRoomID;
	utility::splistString(strRoom, '.', &strRoomArea, &strRoomID);


	//typedef map<string, room> mapAreaRooms;
	//typedef map<string, mapAreaRooms> mapArea;
	
	//Check Area is exist or not.
	mapArea::iterator itArea;
	itArea = Area.find(strRoomArea);
    if (itArea != Area.end())
    {
		//AREA is exist.
		//Get the MapRooms.
		mapAreaRooms::iterator itRooms;
		itRooms = (itArea->second).find(strRoomID);
		if (itRooms != (itArea->second).end())
		{
			//MapRooms is exist, return Room
			return &(itRooms->second);
		}
		else
		{
			cout << "Load [ " << strRoomID << " ]." << endl;
			room _r = room(strRoom);
			//put room to the area
			(itArea->second).insert(pair<string, room>(strRoomID, _r));
			mapAreaRooms::iterator itRooms;
			itRooms = (itArea->second).find(strRoomID);
			return &(itRooms->second);
		}
	}
	else
	{
		//Area not found.
		cout << "[ " << strRoomArea << " ] AREA Not Found. ";
		cout << "Load [ " << strRoomID << " ]." << endl;
		room _r = room(strRoom);
		//put room to the Room map.
		mapAreaRooms mr;
		mr.insert( pair<string, room>(strRoomID, _r) );
		Area.insert(pair<string, mapAreaRooms>(strRoomArea, mr));
		mapAreaRooms::iterator itRooms;
		itRooms = mr.find(strRoomID);
		return &(itRooms->second);
	}
}


bool room::loadRoom(string strRoom)
{
	string strRoomArea;
	string strRoomID;
	utility::splistString(strRoom, '.', &strRoomArea, &strRoomID);
	mapAreaRooms MapRooms;
	mapAreaRooms* pMapRooms;
	
	//create a new room
	room r = room(strRoom);
	if (r.isLoadSuccess)
	{
		//Check Area is exist or not.
		mapArea::iterator itArea;
		itArea = Area.find(strRoomArea);
	    if (itArea == Area.end())
	    {
			//AREA not exist.
			//Defind a RoomMap.
			mapAreaRooms NewRooms;
			//insert the new room to it's RoomMap.
			//NewRooms[strRoomID] = r;
			NewRooms.insert(pair<string,room>(strRoomID, r));
			//insert the RoomMap into Area.
			Area[strRoomArea] = NewRooms;
	    }else{
			//AREA is exist.
			//Get the MapRooms.
			pMapRooms = &(itArea->second);
			(*pMapRooms).insert(pair<string,room>(strRoomID, r));
			//MapRooms = itArea->second;
			//insert to the MapRoom.
			//MapRooms[strRoomID] = r;
	    }
		std::cout << "Load room [" << strRoom << "] success." << std::endl;
	}
	else
	{
		std::cout << "Load room [" << strRoom << "] failed." << std::endl;
	}
}
